import ezdxf
import os
from ezdxf.addons import Importer
from pathlib import Path
import yaml
from datetime import datetime
# from collections import defaultdict
from collections import namedtuple
import math
import pprint
import hashlib

DxfSize = namedtuple('DxfSize', 'left buttom right top width height')
DxfFileInfo = namedtuple('DxfFileInfo', 'full d1 d2 name')

# PADDING_RATE = 0.9
# print(f'grid x = {GRID_WIDTH}')
# print(f'grid y = {GRID_HEIGHT}')
# string_input_with_date = "20/10/2021"
# past = datetime.strptime(string_input_with_date, "%d/%m/%Y")
# present = datetime.now()
# if present.date() >= past.date():
#     exit(0)
# Create a new DXF document.
SCAN_SHAPES = 'CIRCLE LWPOLYLINE DIMENSION LINE ARC INSERT ELLIPSE MTEXT TEXT'


def parse_yaml(file):
    with open(file, 'r', encoding='utf8', errors='ignore') as f:
        return yaml.safe_load(f)


config = parse_yaml('config.yaml')
pprint.pprint(config)
LARGE_GRID_WIDTH = int(config['grid']['large']['width'])
LARGE_GRID_HEIGHT = int(config['grid']['large']['height'])
GRID_WIDTH = int(config['grid']['normal']['width'])
GRID_HEIGHT = int(config['grid']['normal']['height'])
TEXT_FOLDER_HEIGHT = int(config['text']['folder']['height'])
TEXT_IMAGE_HEIGHT = int(config['text']['image']['height'])
TEXT_IMAGE_RIGHT_X = int(config['text']['image']['right_x'])
TEXT_IMAGE_ROWS = int(config['text']['image']['rows'])
TEXT_MARGIN_RATE = 1.3
TEXT_FOLDER_LEFT_X = int(config['text']['folder']['left_x'])
output_dxf = config['output']['normal_dxf']
output_large_dxf = config['output']['large_dxf']
output_large_dxf = config['output']['large_dxf']
logs_file = config['output']['logs_file']
input_dirs = config['input_dirs']
open(logs_file, "w").close()


def append_output(line):
    with open(logs_file, "a", encoding='utf-8') as fw:
        fw.write(line + os.linesep)


def scan_size(dxf_full):
    doc = ezdxf.readfile(dxf_full)
    ents = doc.modelspace().query(SCAN_SHAPES)
    xs = []
    ys = []
    for e in ents:
        # print(e)
        if e.dxftype() == 'LWPOLYLINE':
            # print(e)
            for p in e:
                # print(p)
                xs.append(p[0])
                ys.append(p[1])
        elif e.dxftype() == 'CIRCLE':
            # print(e)
            # print(e.dxf.__dict__)
            cx, cy, _ = e.dxf.center
            # print(e.dxf.center)
            r = e.dxf.radius
            # print(e.dxf.radius)
            xs.append(cx - r)
            xs.append(cx + r)
            ys.append(cy - r)
            ys.append(cy + r)
        elif e.dxftype() == 'DIMENSION':
            # print(e)
            # print(e.dxf.__dict__)
            x, y, _ = e.dxf.defpoint
            xs.append(x)
            ys.append(y)
            x, y, _ = e.dxf.text_midpoint
            xs.append(x)
            ys.append(y)
            x, y, _ = e.dxf.defpoint2
            xs.append(x)
            ys.append(y)
            x, y, _ = e.dxf.defpoint3
            xs.append(x)
            ys.append(y)
        elif e.dxftype() == 'LINE':
            # print(e)
            # print(e.dxf.__dict__)
            x, y, _ = e.dxf.start
            xs.append(x)
            ys.append(y)
            x, y, _ = e.dxf.end
            xs.append(x)
            ys.append(y)
        elif e.dxftype() == 'ELLIPSE':
            # print(e)
            # print(e.dxf.__dict__)
            x, y, _ = e.dxf.center
            xs.append(x)
            ys.append(y)
        elif e.dxftype() == 'INSERT':
            x, y, _ = e.dxf.insert
            xs.append(x)
            ys.append(y)
        elif e.dxftype() == 'MTEXT':
            # print(e.dxf)
            x, y, _ = e.dxf.insert
            xs.append(x)
            ys.append(y)
        elif e.dxftype() == 'TEXT':
            # print(e.dxf)
            x, y, _ = e.dxf.insert
            xs.append(x)
            ys.append(y)
        elif e.dxftype() == 'ARC':
            x, y, _ = e.dxf.center
            xs.append(x)
            ys.append(y)
        else:
            print(e)
            print(e.dxf.__dict__)
    min_x = min(xs)
    min_y = min(ys)
    max_x = max(xs)
    max_y = max(ys)
    return DxfSize(min_x, min_y, max_x, max_y, max_x - min_x, max_y - min_y)


def scan_files(dxf_dir):
    print(f'正在搜索文件夹{dxf_dir}')
    normal_size_lst = []
    large_size_lst = []

    for f1 in os.listdir(dxf_dir):
        # print(f)
        d1_full = os.path.join(dxf_dir, f1)

        if not os.path.isdir(d1_full):
            if f1.endswith('.dxf'):
                append_output(f'不是第一层文件夹，跳过: {d1_full}')
            continue

        print(d1_full)
        normals = []
        larges = []
        for f2 in os.listdir(d1_full):
            dxf_full = os.path.join(d1_full, f2)
            if os.path.isdir(dxf_full):
                append_output(f'超过两层文件夹，跳过: {dxf_full}')

            if not f2.endswith('.dxf'):
                # append_output(f'{f2}不是.dxf，跳过。')
                continue
            name, ext = os.path.splitext(f2)
            dxf_size = scan_size(dxf_full)
            info = (DxfFileInfo(dxf_full, dxf_dir, f1, name), dxf_size)

            if dxf_size.height > LARGE_GRID_HEIGHT:
                append_output(f'{dxf_full} 超高，跳过。')
            elif dxf_size.height > GRID_HEIGHT:
                larges.append(info)
            else:
                normals.append(info)
            print(dxf_full)
        if normals:
            normal_size_lst.append((f1, normals))
        if larges:
            large_size_lst.append((f1, larges))
    return normal_size_lst, large_size_lst


def scan_input_dirs():
    all_normals = []
    all_larges = []
    for d in input_dirs:
        normal_size_lst, large_size_lst = scan_files(d)
        for f1, normals in normal_size_lst:
            all_normals.append((os.path.basename(d), f1, normals))
        for f1, larges in large_size_lst:
            all_larges.append((os.path.basename(d), f1, larges))
    return all_normals, all_larges


def calc_design_width(width, grid_width):
    return math.ceil(width / grid_width) * grid_width


def get_sha1_8_of_string(string):
    return hashlib.sha1(string.encode()).hexdigest()[:8]


def import_file(doc, grid_width, grid_height, pos_x, pos_y, v):
    info, size = v
    print(f'importing {info.full}')
    sdoc = ezdxf.readfile(info.full)
    importer = Importer(sdoc, doc)

    ents = sdoc.modelspace().query('CIRCLE LWPOLYLINE DIMENSION LINE ARC INSERT ELLIPSE MTEXT TEXT')
    # print(ents)
    min_x, min_y, max_x, max_y = size.left, size.buttom, size.right, size.top
    block_name = get_sha1_8_of_string(info.full)
    tblock = doc.blocks.new(block_name)
    importer.import_entities(ents, tblock)
    # importer.import_modelspace()
    # importer.import_paperspace_layouts()
    importer.finalize()
    doc.modelspace().add_blockref(block_name,
                                  (pos_x - min_x + (grid_width - (max_x - min_x)) / 2,
                                   pos_y - min_y + (grid_height - (max_y - min_y)) / 2))


def add_text(msp, pos_x, pos_y, text_height, text):
    mtext = msp.add_mtext(text)
    mtext.dxf.char_height = text_height
    mtext.dxf.insert = pos_x, pos_y + text_height * TEXT_MARGIN_RATE
    mtext.dxf.width = GRID_WIDTH * 10


def merge(lst, grid_width, grid_height, target_dxf_full):
    doc = ezdxf.new(dxfversion="R2010")
    msp = doc.modelspace()

    total_height = grid_height * len(lst)
    current_y = total_height / 2

    for d, f1, vs in lst:
        current_x = 0
        # folder = d + '---' + f1
        # print(folder)
        add_text(msp, current_x + TEXT_FOLDER_LEFT_X, current_y + grid_height / 3, TEXT_FOLDER_HEIGHT, d)
        add_text(msp, current_x + TEXT_FOLDER_LEFT_X, current_y + grid_height * 2 / 3, TEXT_FOLDER_HEIGHT, f1)
        top = current_y + grid_height

        # folder buttom
        msp.add_line((current_x + TEXT_FOLDER_LEFT_X, current_y), (current_x, current_y))
        # folder top
        msp.add_line((current_x + TEXT_FOLDER_LEFT_X, top), (current_x, top))
        # x=0 v
        msp.add_line((current_x, current_y), (current_x, current_y + grid_height))
        for v in vs:
            delta_x = calc_design_width(v[1].width, grid_width)
            text_y = current_y
            if TEXT_IMAGE_ROWS > 2:
                add_text(msp, current_x + TEXT_IMAGE_RIGHT_X, text_y, TEXT_IMAGE_HEIGHT, d)
                text_y += TEXT_IMAGE_HEIGHT * TEXT_MARGIN_RATE
            if TEXT_IMAGE_ROWS > 1:
                add_text(msp, current_x + TEXT_IMAGE_RIGHT_X, text_y, TEXT_IMAGE_HEIGHT, f1)
                text_y += TEXT_IMAGE_HEIGHT * TEXT_MARGIN_RATE
            add_text(msp, current_x + TEXT_IMAGE_RIGHT_X, text_y, TEXT_IMAGE_HEIGHT, v[0].name)

            import_file(doc, delta_x, grid_height, current_x, current_y, v)
            right = current_x + delta_x
            # top
            msp.add_line((current_x, top), (right, top))
            # buttom
            msp.add_line((current_x, current_y), (right, current_y))
            # r v
            msp.add_line((right, current_y), (right, current_y + grid_height))
            current_x += delta_x
        current_y -= grid_height

    doc.saveas(target_dxf_full)
    print(f'merged output dxf: {target_dxf_full}')


if __name__ == '__main__':
    all_normals, all_larges = scan_input_dirs()
    if all_normals:
        merge(all_normals, GRID_WIDTH, GRID_HEIGHT, output_dxf)
    if all_larges:
        merge(all_larges, LARGE_GRID_WIDTH, LARGE_GRID_HEIGHT, output_large_dxf)
    print(f'logs output: {logs_file}')
    print('press any key to exit')
    input()
